package pp.infrastructure.util.stringhelper;

import lombok.extern.slf4j.Slf4j;
import pp.infrastructure.exception.PPException;
import pp.infrastructure.exception.PPExceptionInfo;
import pp.infrastructure.redis.RedisHelper;
import pp.infrastructure.util.SpringBeanHolder;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

@Slf4j
public class StringHelper {

    private static RedisHelper redisHelper;

    // 保证 RedisHelper 的正确加载
    private static RedisHelper getRedisHelper(){
        if(StringHelper.redisHelper == null) {
            synchronized (StringHelper.class) {
                if(StringHelper.redisHelper == null) {
                    StringHelper.redisHelper = SpringBeanHolder.getBean(RedisHelper.class);
                }
            }
        }
        return redisHelper;
    }

    /**
     * 默认方式生成随机字符串（字母与数字组成）
     * @param length     字符串长度
     * @param aliveTime  存活时长
     * @return           返回随机字符串
     */
    public static String generateRandomString(int length, long aliveTime) {
        return generateRandomString(length, StringHelperConstant.DEFAULT_CHARSET, aliveTime);
    }

    /**
     * 生成随机字符串
     * @param length     字符串长度
     * @param charset    字符串组成字符集
     * @param aliveTime  存活时长
     * @return           返回随机字符串
     */
    public static String generateRandomString(int length, String charset, long aliveTime) {
        if (length < StringHelperConstant.MIN_LENGTH || length > StringHelperConstant.MAX_LENGTH) {
           log.error(String.valueOf(PPExceptionInfo.STRING_ERROR_LENGTH));
           throw new PPException(PPExceptionInfo.STRING_ERROR_LENGTH);
        }
        if (aliveTime < StringHelperConstant.MIN_ALIVETIME || aliveTime > StringHelperConstant.MAX_ALIVETIME) {
            log.error(String.valueOf(PPExceptionInfo.STRING_ERROR_ALIVETIME));
            throw new PPException(PPExceptionInfo.STRING_ERROR_ALIVETIME);
        }
        try {
            SecureRandom random = new SecureRandom();
            StringBuilder stringBuilder = new StringBuilder(length);
            String randomString = "";
            do {
                stringBuilder.setLength(0);
                for (int i = 0; i < length; i++) {
                    stringBuilder.append(charset.charAt(random.nextInt(0, charset.length())));
                }
                randomString = stringBuilder.toString();
            } while (getRedisHelper().exists(StringHelperConstant.REDIS_KEY_PREFIX+randomString));
            getRedisHelper().setCache(StringHelperConstant.REDIS_KEY_PREFIX+randomString, randomString, aliveTime);
            return randomString;
        } catch (RuntimeException e) {
            log.error(String.valueOf(PPExceptionInfo.GENERIC_SYSTEM_FAIL));
            throw new PPException(PPExceptionInfo.GENERIC_SYSTEM_FAIL);
        }
    }

    /**
     * 字符串加密
     * @param data 需要加密的信息
     * @return  返回密文
     */
    public static String encrypt(String data){
        if (data == null) {
            log.error(String.valueOf(PPExceptionInfo.STRING_ERROR_DATA));
            throw new PPException(PPExceptionInfo.STRING_ERROR_DATA);
        }
        try{
            SecretKeySpec secretKey = new SecretKeySpec(StringHelperConstant.KEY.getBytes(StandardCharsets.UTF_8), StringHelperConstant.SECRET_METHOD);
            Cipher cipher = Cipher.getInstance(StringHelperConstant.SECRET_METHOD);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] encryptData = cipher.doFinal(data.getBytes());
            return Base64.getEncoder().encodeToString(encryptData);
        } catch (Exception e) {
            log.error(String.valueOf(PPExceptionInfo.GENERIC_SYSTEM_FAIL));
            throw new PPException(PPExceptionInfo.GENERIC_SYSTEM_FAIL);
        }
    }

    /**
     * 密文解密
     * @param encryptedData 密文
     * @return 返回加密前的真实信息
     */
    public static String decrypt(String encryptedData) {
        if (encryptedData == null) {
            log.error(String.valueOf(PPExceptionInfo.STRING_ERROR_DATA));
            throw new PPException(PPExceptionInfo.STRING_ERROR_DATA);
        }
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(StringHelperConstant.KEY.getBytes(StandardCharsets.UTF_8), StringHelperConstant.SECRET_METHOD);
            Cipher cipher = Cipher.getInstance(StringHelperConstant.SECRET_METHOD);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            byte[] decodeData = Base64.getDecoder().decode(encryptedData);
            byte[] decryptedData = cipher.doFinal(decodeData);
            return new String(decryptedData, StandardCharsets.UTF_8);
        } catch (Exception e){
            log.error(String.valueOf(PPExceptionInfo.GENERIC_SYSTEM_FAIL));
            throw new PPException(PPExceptionInfo.GENERIC_SYSTEM_FAIL);
        }
    }
}
